package com.example.myproject.distributionlock.springboot.case1;

import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import redis.clients.jedis.Jedis;

import java.util.Collections;

/**
 * 只能解决单个redis的情况，集群需要使用redission
 */
public class RedisLockLuaUtil {
    private static final Long SUCCESS = 1L;
    private static RedisTemplate redisTemplate;

    //还需要考虑重试机制
    public static void tryLock(String key,String value){
        redisTemplate.execute(new RedisCallback() {
            @Override
            public Object doInRedis(RedisConnection connection) throws DataAccessException {
                //connection.setNX()
                Jedis jedis = (Jedis) connection.getNativeConnection();
                String status = jedis.set(key, value, "nx", "ex", 5);
                return status;
            }
        });
    }

    /**
     * lua 脚本缺点：key过期之后，但是A程序比较耗时，就会有问题，所以要设置下短时间的。
     * lua目前是原子性的，执行lua脚本之后，其他redis命令都要等待；
     * 但是lua最好是一条命令，防止redis突然挂了，后面的命令没执行；
     * 获取锁
     * 其实 Redis 已经考虑到了大家的疾苦，从 2.6.12 起，SET 涵盖了 SETEX 的功能，并且 SET 本身已经包含了设置过期时间的功能，也就是说，我们前面需要的功能只用 SET 就可以实现。
     *
     * @param lockKey
     * @param value
     * @param expireTime：单位-秒
     * @return
     */
    public static boolean tryLock(String lockKey, String value, int expireTime){
        boolean ret = false;
        try{
            //String script = "if redis.call('setNx',KEYS[1],ARGV[1]) then if redis.call('get',KEYS[1])==ARGV[1] then return redis.call('expire',KEYS[1],ARGV[2]) else return 0 end end";
            String script ="redis.call('SET', KEYS[1], ARGV[1], 'NX', 'PX', ARGV[2])";
            RedisScript<String> redisScript = new DefaultRedisScript<>(script, String.class);
            Object result = redisTemplate.execute(redisScript, Collections.singletonList(lockKey),value,expireTime);
            if(SUCCESS.equals(result)){
                 return true;
            }
        }catch(Exception e){
        }
        return ret;
    }

    /**
     * 释放锁
     * @param lockKey
     * @param value
     * @return
     */
    public static boolean releaseLock(String lockKey, String value) {
            String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
            RedisScript<String> redisScript = new DefaultRedisScript<>(script, String.class);
            Object result = redisTemplate.execute(redisScript, Collections.singletonList(lockKey), value);
            if (SUCCESS.equals(result)) {
                  return true;
            }
            return false;
       }

}
